﻿using System;
using System.Collections.Generic;
using System.Data.Linq;
using System.Linq;
using sBlog.Net.Domain.Entities;
using sBlog.Net.Domain.Interfaces;

namespace sBlog.Net.Domain.Concrete
{
    public class Post : DefaultDisposable, IPost
    {
        private readonly Table<PostEntity> _postsTable;
        private readonly ITag _tagRepository;
        private readonly ICategory _categoryRepository;
        private readonly IUser _userRepository;

        public Post(IUser userRepository, ICategory categoryRepository, ITag tagRepository)
        {
            _postsTable = context.GetTable<PostEntity>();
            _tagRepository = tagRepository;
            _categoryRepository = categoryRepository;
            _userRepository = userRepository;
        }

        /// <summary>
        /// Gets all the posts & pages by user ID.
        /// </summary>
        /// <param name="userID">The user ID.</param>
        /// <returns>a list of PostEntity objects</returns>
        public List<PostEntity> GetPostsByUserID(int userID)
        {
            List<PostEntity> postEntities = context.ExecuteQuery<PostEntity>("EXEC GetPostsByUserID {0}", userID).ToList();
            return postEntities;
        }

        public List<PostEntity> ShowReadContentPage()
        {
            List<PostEntity> postEntities = context.ExecuteQuery<PostEntity>("EXEC GetPostsShowReadContentPage").ToList();
            return postEntities;
        }

        public List<PostEntity> GetNewPostByAuthor(int ownerUserID)
        {
            List<PostEntity> postEntities = context.ExecuteQuery<PostEntity>("EXEC GetNewPostByAuthor {0}", ownerUserID).ToList();
            return postEntities;
        }

        public List<PostEntity> GetMostViewPostByCatSlug(string catslug)
        {
            List<PostEntity> postEntities = context.ExecuteQuery<PostEntity>("EXEC GetMostViewPostByCatSlug {0}", catslug).ToList();
            return postEntities;
        }

        public void GetViewAndLikeCount(int postID, ref int viewCount, ref int likeCount)
        {
            PostEntity post = context.ExecuteQuery<PostEntity>("EXEC GetPostById {0}", postID).FirstOrDefault();
            viewCount = post.ViewCount;
            likeCount = post.LikeCount;
        }

        public List<PostEntity> GetPostRelateTag(int postID)
        {
            List<PostEntity> postEntities = context.ExecuteQuery<PostEntity>("EXEC GetPostRelateTag {0}", postID).ToList();
            return postEntities;
        }

        public List<PostEntity> GetPostsByTagWithPage(string tagSlug, int pageSize, int pageNumber)
        {
            List<PostEntity> postEntities = context.ExecuteQuery<PostEntity>("EXEC GetPostsByTagWithPage {0}, {1}, {2}", tagSlug, pageSize, pageNumber).ToList();
            return postEntities;
        }

        public List<PostEntity> GetPostsByUserIdWithPage(int userId, int? pageNumber, int? pageSize)
        {
            List<PostEntity> postEntities = context.ExecuteQuery<PostEntity>("EXEC GetPostsByUserIdWithPage {0}, {1}, {2}", userId, pageSize, pageNumber).ToList();
            return postEntities;
        }

        public List<PostEntity> GetPostsByCategoryWithPage(string catSlug, int pageSize, int pageNumber)
        {
            List<PostEntity> postEntities = context.ExecuteQuery<PostEntity>("EXEC GetPostsByCategoryWithPage {0}, {1}, {2}", catSlug, pageSize, pageNumber).ToList();
            return postEntities;
        }

        public List<PostEntity> GetMostViewPostByCatSlugAndOwnerUserID(int ownerUserID, string catSlug)
        {
            List<PostEntity> postEntities = context.ExecuteQuery<PostEntity>("EXEC GetMostViewPostByCatSlugAndOwnerUserID {0}, {1}", ownerUserID, catSlug).ToList();
            return postEntities;
        }

        public List<PostEntity> SearchKeywordWithPage(string textSearch, int? pageNumber, int? pageSize)
        {
            List<PostEntity> postEntities = context.ExecuteQuery<PostEntity>("EXEC SearchKeywordWithPageForCP {0}, {1}, {2}", textSearch, pageNumber, pageSize).ToList();
            return PostProcessEntities(postEntities);
        }

        /// <summary>
        /// Get the posts identified by the user ID and entry type
        /// 
        /// entryType could be 1 for posts and 2 for pages
        /// 
        /// Used in the "admin" section.
        /// </summary>
        /// <param name="userID">The user ID.</param>
        /// <param name="entryType">Type of the entry.</param>
        /// <returns>a list of PostEntity objects</returns>
        public List<PostEntity> GetPostsByUserID(int userID, byte entryType)
        {
            List<PostEntity> postEntities = context.ExecuteQuery<PostEntity>("EXEC GetPostsByUserIDWithEntryType {0}, {1}", userID, entryType).ToList();
            return postEntities;
        }

        public List<PostEntity> GetPostsByUserIDForCP(int userID, byte entryType)
        {
            List<PostEntity> postEntities = context.ExecuteQuery<PostEntity>("EXEC GetPostsByUserIDForCP {0}, {1}", userID, entryType).ToList();
            return PostProcessEntities(postEntities);
        }

        public List<PostEntity> GetPostsByUserIDAndStatusForCP(int userID, int status, byte entryType)
        {
            List<PostEntity> postEntities = context.ExecuteQuery<PostEntity>("EXEC GetPostsByUserIDAndStatusForCP {0}, {1}, {2}", userID, status, entryType).ToList();
            return PostProcessEntities(postEntities);
        }

        public List<PostEntity> GetPostsByUserIDAndStatusForCPNew(int userID, int status, byte entryType, int pageNumber)
        {
            List<PostEntity> postEntities = context.ExecuteQuery<PostEntity>("EXEC GetPostsByUserIDAndStatusForCPNew {0}, {1}, {2}, {3}", userID, status, entryType, pageNumber).ToList();
            return PostProcessEntities(postEntities);
        }

        public List<PostEntity> GetPostsByIsPrivate(Boolean isPrivate, int pageSize, int pageNumber, int totalPost)
        {
            List<PostEntity> postEntities = context.ExecuteQuery<PostEntity>("EXEC GetPostsByIsPrivate {0}, {1}, {2}, {3}", isPrivate, pageSize, pageNumber, totalPost).ToList();
            return PostProcessEntities(postEntities);
        }

        public List<PostEntity> GetPostsByIsHome(Boolean isHome, int pageSize, int pageNumber, int totalPost)
        {
            List<PostEntity> postEntities = context.ExecuteQuery<PostEntity>("EXEC GetPostsByIsHome {0}, {1}, {2}, {3}", isHome, pageSize, pageNumber, totalPost).ToList();
            return PostProcessEntities(postEntities);
        }

        public List<PostEntity> GetPostsByIsHomeUser(Boolean isHomeUser, int pageSize, int pageNumber, int totalPost)
        {
            List<PostEntity> postEntities = context.ExecuteQuery<PostEntity>("EXEC GetPostsByIsHomeUser {0}, {1}, {2}, {3}", isHomeUser, pageSize, pageNumber, totalPost).ToList();
            return PostProcessEntities(postEntities);
        }

        public List<PostEntity> GetPostsByIsCatHome(Boolean isCatHome, int pageSize, int pageNumber, int totalPost)
        {
            List<PostEntity> postEntities = context.ExecuteQuery<PostEntity>("EXEC GetPostsByIsCatHome {0}, {1}, {2}, {3}", isCatHome, pageSize, pageNumber, totalPost).ToList();
            return PostProcessEntities(postEntities);
        }

        public List<PostEntity> GetPostsByIsFeature(Boolean isFeature, int pageSize, int pageNumber, int totalPost)
        {
            List<PostEntity> postEntities = context.ExecuteQuery<PostEntity>("EXEC GetPostsByIsFeature {0}, {1}, {2}, {3}", isFeature, pageSize, pageNumber, totalPost).ToList();
            return PostProcessEntities(postEntities);
        }

        public List<PostEntity> GetPostsByShowReadPage(Boolean showReadPage, int pageSize, int pageNumber, int totalPost)
        {
            List<PostEntity> postEntities = context.ExecuteQuery<PostEntity>("EXEC GetPostsByShowReadPage {0}, {1}, {2}, {3}", showReadPage, pageSize, pageNumber, totalPost).ToList();
            return PostProcessEntities(postEntities);
        }

        /// <summary>
        /// //
        /// </summary>
        /// <returns></returns>

        public List<PostEntity> GetPostFeature()
        {
            List<PostEntity> postEntities = context.ExecuteQuery<PostEntity>("EXEC GetPostFeature").ToList();
            return postEntities;
        }

        public IEnumerable<PostEntity> GetNewPosts()
        {
            IEnumerable<PostEntity> postEntities = context.ExecuteQuery<PostEntity>("EXEC GetNewPosts").ToList();
            return postEntities;
        }

        public List<PostEntity> GetMostPostByAuthors(int OwnerUserID, int currentPostID)
        {
            List<PostEntity> postEntities = context.ExecuteQuery<PostEntity>("EXEC GetMostPostByAuthors {0}, {1}", OwnerUserID, currentPostID).ToList();
            return postEntities;
        }

        public List<PostEntity> GetHomePageWithPage(int pageSize, int pageNumber)
        {
            List<PostEntity> postEntities = context.ExecuteQuery<PostEntity>("EXEC GetHomePageWithPage {0}, {1}", pageSize, pageNumber).ToList();
            return postEntities;
        }

        public PostEntity GetPostByID(int postID)
        {
            PostEntity post = context.ExecuteQuery<PostEntity>("EXEC GetPostById {0}", postID).FirstOrDefault();
            return post;
        }

        public PostEntity GetPostByUrl(string url, byte entryType)
        {
            PostEntity post = context.ExecuteQuery<PostEntity>("EXEC GetPostByUrlWithEntryType {0}, {1}", url, entryType).FirstOrDefault();
            return post != null ? this.GetPostByID(post.PostID) : null;
        }

        public int GetViewByPostID(int postID)
        {
            return this.GetPostByID(postID).ViewCount;
        }

        public int GetLikeByPostID(int postID)
        {
            return this.GetPostByID(postID).LikeCount;
        }

        public int GetTotalViewPostByUserId(int userID)
        {
            int TotalViewPost = 0;
            foreach (PostEntity p in this.GetPostsByUserID(userID).ToList())
            {
                TotalViewPost = TotalViewPost + p.ViewCount;
            }
            return TotalViewPost;
        }

        public int GetTotalLikePostByUserId(int userID)
        {
            int TotalLikePost = 0;
            foreach (PostEntity p in this.GetPostsByUserID(userID).ToList())
            {
                TotalLikePost = TotalLikePost + p.LikeCount;
            }
            return TotalLikePost;
        }

        public List<PostEntity> GetPosts()
        {
            var postEntities = context.ExecuteQuery<PostEntity>("EXEC GetPostsInternal {0}", -1).ToList();
            return PostProcessEntities(postEntities);
        }

        public List<PostEntity> GetPostWidgetHocMai(int numPost, string catslug)
        {
            var postEntities = context.ExecuteQuery<PostEntity>("EXEC GetPostWidgetHocMai {0}, {1}", numPost, catslug).ToList();
            return postEntities;
        }

        public List<PostEntity> GetPosts(int userID)
        {
            List<PostEntity> postEntities = _postsTable.Where(p => (!p.IsPrivate && p.EntryType == 1) || (p.IsPrivate && p.EntryType == 1 && p.OwnerUserID == userID) && p.PublicDate <= DateTime.Now)
                             .OrderByDescending(p => p.PublicDate)
                             .ToList();
            return postEntities;
        }


        public List<PostEntity> GetPostsByCategory(string categoryName)
        {
            var postEntities = context.ExecuteQuery<PostEntity>("EXEC GetPostsByCategory {0}", categoryName).ToList();
            return PostProcessEntities(postEntities);
        }

        public List<PostEntity> GetPosts(int excludeUserID, byte entryType)
        {
            var postEntities = _postsTable.Where(p => p.EntryType == entryType && p.OwnerUserID != excludeUserID && !p.IsPrivate && p.PublicDate <= DateTime.Now)
                                                      .ToList();
            return PostProcessEntities(postEntities);
        }

        public List<PostEntity> GetPages()
        {
            var postEntities = _postsTable.Where(p => !p.IsPrivate && p.EntryType == 2)
                               .ToList();
            return PostProcessEntities(postEntities);
        }

        public List<PostEntity> GetPages(int userID)
        {
            var postEntities = _postsTable.Where(p => (!p.IsPrivate && p.EntryType == 2) || (p.IsPrivate && p.EntryType == 2 && p.OwnerUserID == userID))
                               .ToList();
            return PostProcessEntities(postEntities);
        }

        public List<PostEntity> GetAllPostsOrPages(bool includeAll)
        {
            var postEntities = _postsTable.ToList();
            return !includeAll ? postEntities : PostProcessEntities(postEntities);
        }

        public void DeletePost(int postID)
        {
            var post = _postsTable.SingleOrDefault(p => p.PostID == postID);

            if (post != null)
            {
                _tagRepository.DeleteTagsForPost(postID);
                _categoryRepository.DeletePostCategoryMapping(postID);
                _postsTable.DeleteOnSubmit(post);
                context.SubmitChanges();
            }
        }

        public void UpdatePost(PostEntity postEntity)
        {
            try
            {
                UpdatePostInternal(postEntity);
                _categoryRepository.UpdatePostCategoryMapping(postEntity.Categories, postEntity.PostID);

                if (postEntity.Tags != null)
                    _tagRepository.UpdateTagsForPost(postEntity.Tags, postEntity.PostID);
            }
            catch
            {

            }
        }

        public void UpdateViewCount(int postID)
        {
            context.ExecuteCommand("EXEC Post_UpdateView {0}", postID);
        }

        public int UpdateLikeCount(int postID, int count)
        {
            PostEntity post = _postsTable.SingleOrDefault(p => p.PostID == postID);

            if (post != null)
            {
                post.LikeCount = post.LikeCount + count;
                UpdatePostInternal(post);
            }

            return post.LikeCount;
        }

        /// <summary>
        /// Delete all posts identified by the user ID.
        /// </summary>
        /// <param name="userID">The user ID.</param>
        public void DeletePostsByUserID(int userID)
        {
            var posts = _postsTable.Where(p => p.OwnerUserID == userID);
            if (posts.Any())
            {
                _postsTable.DeleteAllOnSubmit(posts);
                context.SubmitChanges();
            }
        }

        /// <summary>
        /// Add a post. It also should take care of cateogries/tags.
        /// </summary>
        /// <param name="postEntity">The post entity.</param>
        /// <returns></returns>
        public int AddPost(PostEntity postEntity)
        {
            var postID = -1;
            try
            {
                // add the post
                postID = AddPostInternal(postEntity);

                // add the categories
                _categoryRepository.AddPostCategoryMapping(postEntity.Categories, postID);

                // add the tags
                if (postEntity.Tags != null)
                    _tagRepository.AddTagsForPost(postEntity.Tags, postID);

                // return the id
                return postID;
            }
            catch
            {
                // delete categories/tags if any                
                if (postID > 0)
                {
                    _categoryRepository.DeletePostCategoryMapping(postID);
                    _tagRepository.DeleteTagsForPost(postID);
                }

                // delete the post
                DeletePost(postID);

                return -1;
            }
        }

        private int AddPostInternal(PostEntity postEntity)
        {
            _postsTable.InsertOnSubmit(postEntity);
            context.SubmitChanges();
            return postEntity.PostID;
        }

        private void UpdatePostInternal(PostEntity postEntity)
        {
            var post = _postsTable.SingleOrDefault(p => p.PostID == postEntity.PostID);
            if (post != null)
            {
                post.PostTitle = postEntity.PostTitle;
                post.PostAvatar = postEntity.PostAvatar;
                post.PostAvatarFeature = postEntity.PostAvatarFeature;
                post.PostAvatarSquare = postEntity.PostAvatarSquare;
                post.PostAvatarYanNews = postEntity.PostAvatarYanNews;
                post.PostDescription = postEntity.PostDescription;
                post.PostContent = postEntity.PostContent;
                post.PostUrl = postEntity.PostUrl;
                post.PostEditedDate = postEntity.PostEditedDate;
                post.UserCanAddComments = postEntity.UserCanAddComments;
                post.CanBeShared = postEntity.CanBeShared;
                post.IsPrivate = postEntity.IsPrivate;
                post.EntryType = postEntity.EntryType;
                post.Order = postEntity.Order.HasValue ? postEntity.Order.Value : (int?)null;
                post.LikeCount = postEntity.LikeCount;
                post.ViewCount = postEntity.ViewCount;
                post.Status = postEntity.Status;
                post.PublicDate = postEntity.PublicDate;
                post.IsHome = postEntity.IsHome;
                post.IsCatHome = postEntity.IsCatHome;
                post.IsHomeUser = postEntity.IsHomeUser;
                post.VideoUrl = postEntity.VideoUrl;
                post.IsFeature = postEntity.IsFeature;
                post.Promote = postEntity.Promote;
                post.Order = postEntity.OrderNumber;
                post.PromoteType = postEntity.PromoteType;
                post.ShowReadPage = postEntity.ShowReadPage;
                post.IsYanNews = postEntity.IsYanNews;
                post.CatId = postEntity.CatId;
                post.ZoneId = postEntity.ZoneId;
                post.MChoice = postEntity.MChoice;
                post.Folder = postEntity.Folder;
                context.SubmitChanges();
            }
        }

        private List<PostEntity> PostProcessEntities(List<PostEntity> postEntities)
        {
            postEntities.ForEach(p =>
            {
                p.Categories = _categoryRepository.GetCategoriesByPostID(p.PostID);
                p.Tags = _tagRepository.GetTagsByPostID(p.PostID);

                var user = _userRepository.GetUserObjByUserID(p.OwnerUserID);
                p.OwnerUserName = user.UserDisplayName;
                p.UserEmailAddress = user.UserEmailAddress;
                p.FacebookUrl = user.FacebookUrl;
                p.UserName = user.UserName;
                p.UserAvatar = user.Avatar;
            });
            return postEntities;
        }

        ~Post()
        {
            Dispose(false);
        }
    }
}
